package com.awesome.auth.config;

import com.awesome.auth.service.authentication.*;
import com.awesome.auth.service.interfaces.UsernamePasswordUserDetailService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 *                     .::::.
 *                   .::::::::.
 *                  :::::::::::    佛主保佑、永无Bug
 *              ..:::::::::::'
 *            '::::::::::::'
 *              .::::::::::
 *         '::::::::::::::..
 *              ..::::::::::::.
 *           ``::::::::::::::::
 *             ::::``:::::::::'        .:::.
 *            ::::'   ':::::'       .::::::::.
 *         .::::'      ::::     .:::::::'::::.
 *        .:::'       :::::  .:::::::::' ':::::.
 *       .::'        :::::.:::::::::'      ':::::.
 *      .::'         ::::::::::::::'         ``::::.
 *  ...:::           ::::::::::::'              ``::.
 * ```` ':.          ':::::::::'                  ::::..
 *                   '.:::::'                    ':'````..
 * @version: V1.0
 * @author: qxw
 * @description: 用于保护oauth相关的endpoints(端点），同时主要作用于用户的登录(form login）
 * @data: 2020年04月11日 16:19
 * @ModifyDate: 2020年04月11日 16:19
 **/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private static final Logger LOG = LoggerFactory.getLogger(WebSecurityConfig.class);

    @Autowired
    CustomLogoutSuccessHandler customLogoutSuccessHandler;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UsernamePasswordUserDetailService usernamePasswordUserDetailService;

    /**
     * 匹配 "/" 路径，不需要权限即可访问
     * 匹配 "/user" 及其以下所有路径，都需要 "USER" 权限
     * 登录地址为 "/login"，登录成功默认跳转到页面 "/user"
     * 退出登录的地址为 "/logout"，退出成功后跳转到页面 "/login"
     * 默认启用 CSRF
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 默认支持./login实现authorization_code认证
         * formLogin().loginPage("/login") 指定支持基于表单的身份验证
         * 注意：loginPage和loginProcessingUrl两者都不配置：默认都是/login
         * 两者都配置：按自己的来
         *
         */
        http
            .formLogin().loginPage("/index.html").loginProcessingUrl("/login")
            .and()
            .authorizeRequests()
            .antMatchers("/index.html", "/login", "/resources/**", "/static/**").permitAll()
            .anyRequest() // 任何请求
            .authenticated()// 都需要身份认证
            .and()
            .logout().invalidateHttpSession(true).deleteCookies("JSESSIONID").logoutSuccessHandler(customLogoutSuccessHandler).permitAll()
            .and()
            .csrf().disable();

        LOG.info("首先进行登录页面相关配置");
        LOG.info("㊀配置自定义登录页面为index.html，后端用户名及密码验证为\"/login\"");
        LOG.info("㊁因为为自定义登录页面，配置\"/index.html\", \"/login\", \"/resources/**\", \"/static/\"不需要认证，其它请求需要认证。否则登录页面无法打开");
        LOG.info("㊂配置退出登录时，删除session和cookie，并自定义退出成功后的handler");


        // 自定义认证filter，支持./oauth/custom/token实现(授权码模式)authorization_code认证
        http.addFilterAfter(externalAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * [用户名密码的认证]
     * 1、UsernamePasswordAuthenticationFilter 拦截器匹配/login的POST请求，保存认证信息(用户名和密码)
     * 2、最终由用户实现UsernamePasswordAuthenticationProvider(authenticate方法) 进行实际认证
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) {
        // 定义认证的provider用于实现用户名和密码认证
        auth.authenticationProvider(new UsernamePasswordAuthenticationProvider(usernamePasswordUserDetailService));
        // 自定义provider用于实现自定义的登录认证
        auth.authenticationProvider(new CustomerAuthenticationProvider());

        LOG.info("㊀入自定义的UsernamePasswordAuthenticationProvider，用于实现用户名和密码认证");
    }

    @Bean
    public CustomAuthenticationProcessingFilter externalAuthenticationProcessingFilter() {
        // 自定义认证filter，需要实现CustomAuthenticationProcessingFilter和CustomerAuthenticationProvider
        // filter将过滤url并把认证信息塞入authentication作为CustomerAuthenticationProvider.authenticate的入参
        CustomAuthenticationProcessingFilter filter = new CustomAuthenticationProcessingFilter();

        // 默认自定义认证方式grant_type为authorization_code方式，如果直接返回内容，则需自定义success和fail handler
        // filter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
        // filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());

        filter.setAuthenticationManager(authenticationManager);
        return filter;
    }

    /**
     * 需要配置这个支持password模式
     * support password grant type
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}